﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using TNet;
using UnityEngine;

[ExecuteInEditMode, AddComponentMenu("TNet/Network Object")]
public sealed class TNObject : MonoBehaviour
{
    [SerializeField]
    private int id;
    private static TNet.List<DelayedCall> mDelayed = new TNet.List<DelayedCall>();
    private static Dictionary<uint, TNObject> mDictionary = new Dictionary<uint, TNObject>();
    private static int mDummyID = 0;
    [NonSerialized]
    private bool mIsRegistered;
    private static TNet.List<TNObject> mList = new TNet.List<TNObject>();
    [NonSerialized]
    private int mOwner = -1;
    [NonSerialized]
    private TNObject mParent;
    [NonSerialized]
    private TNet.List<CachedFunc> mRFCs = new TNet.List<CachedFunc>();
    [NonSerialized, HideInInspector]
    public bool rebuildMethodList = true;

    private void Awake()
    {
        if ((this.id == 0) && !TNManager.isConnected)
        {
            this.id = ++mDummyID;
        }
        this.mOwner = TNManager.objectOwnerID;
        if (TNManager.players.size == 0)
        {
            this.mOwner = TNManager.playerID;
        }
        else if (TNManager.GetPlayer(this.mOwner) == null)
        {
            this.mOwner = TNManager.hostID;
        }
    }

    public void BroadcastToLAN(int port, byte rfcID, params object[] objs)
    {
        this.BroadcastToLAN(port, rfcID, null, objs);
    }

    public void BroadcastToLAN(int port, string rfcName, params object[] objs)
    {
        this.BroadcastToLAN(port, 0, rfcName, objs);
    }

    private void BroadcastToLAN(int port, byte rfcID, string rfcName, params object[] objs)
    {
        BinaryWriter bw = TNManager.BeginSend(Packet.ForwardToAll);
        bw.Write(GetUID(this.uid, rfcID));
        if (rfcID == 0)
        {
            bw.Write(rfcName);
        }
        bw.WriteArray(objs);
        TNManager.EndSend(port);
    }

    public static void DecodeUID(uint uid, out uint objID, out byte rfcID)
    {
        rfcID = (byte) (uid & 0xff);
        objID = uid >> 8;
    }

    public void DestroySelf()
    {
        base.StartCoroutine(this.EnsureDestroy());
        TNManager.Destroy(base.gameObject);
    }

    [DebuggerHidden]
    private IEnumerator EnsureDestroy()
    {
        return new <EnsureDestroy>c__Iterator68 { <>f__this = this };
    }

    public bool Execute(byte funcID, params object[] parameters)
    {
        if (this.mParent != null)
        {
            return this.mParent.Execute(funcID, parameters);
        }
        if (this.rebuildMethodList)
        {
            this.RebuildMethodList();
        }
        return UnityTools.ExecuteAll(this.mRFCs, funcID, parameters);
    }

    public bool Execute(string funcName, params object[] parameters)
    {
        if (this.mParent != null)
        {
            return this.mParent.Execute(funcName, parameters);
        }
        if (this.rebuildMethodList)
        {
            this.RebuildMethodList();
        }
        return UnityTools.ExecuteAll(this.mRFCs, funcName, parameters);
    }

    public static TNObject Find(uint tnID)
    {
        if (mDictionary == null)
        {
            return null;
        }
        TNObject obj2 = null;
        mDictionary.TryGetValue(tnID, out obj2);
        return obj2;
    }

    public static void FindAndExecute(uint objID, byte funcID, params object[] parameters)
    {
        TNObject obj2 = Find(objID);
        if (obj2 != null)
        {
            if (obj2.Execute(funcID, parameters))
            {
            }
        }
        else if (TNManager.isInChannel)
        {
            DelayedCall item = new DelayedCall {
                objID = objID,
                funcID = funcID,
                parameters = parameters
            };
            mDelayed.Add(item);
        }
    }

    public static void FindAndExecute(uint objID, string funcName, params object[] parameters)
    {
        TNObject obj2 = Find(objID);
        if (obj2 != null)
        {
            if (obj2.Execute(funcName, parameters))
            {
            }
        }
        else if (TNManager.isInChannel)
        {
            DelayedCall item = new DelayedCall {
                objID = objID,
                funcName = funcName,
                parameters = parameters
            };
            mDelayed.Add(item);
        }
    }

    private static TNObject FindParent(Transform t)
    {
        while (t != null)
        {
            TNObject component = t.gameObject.GetComponent<TNObject>();
            if (component != null)
            {
                return component;
            }
            t = t.parent;
        }
        return null;
    }

    private static uint GetUID(uint objID, byte rfcID)
    {
        return ((objID << 8) | rfcID);
    }

    private void OnDestroy()
    {
        this.Unregister();
    }

    private void OnNetworkPlayerLeave(Player p)
    {
        if ((p != null) && (this.mOwner == p.id))
        {
            this.mOwner = TNManager.hostID;
        }
    }

    private void RebuildMethodList()
    {
        this.rebuildMethodList = false;
        this.mRFCs.Clear();
        MonoBehaviour[] componentsInChildren = base.GetComponentsInChildren<MonoBehaviour>(true);
        int index = 0;
        int length = componentsInChildren.Length;
        while (index < length)
        {
            MonoBehaviour behaviour = componentsInChildren[index];
            MethodInfo[] methods = behaviour.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            for (int i = 0; i < methods.Length; i++)
            {
                if (methods[i].IsDefined(typeof(TNet.RFC), true))
                {
                    CachedFunc item = new CachedFunc {
                        obj = behaviour,
                        func = methods[i]
                    };
                    TNet.RFC rfc = (TNet.RFC) item.func.GetCustomAttributes(typeof(TNet.RFC), true)[0];
                    item.id = rfc.id;
                    this.mRFCs.Add(item);
                }
            }
            index++;
        }
    }

    public void Register()
    {
        if ((!this.mIsRegistered && (this.uid != 0)) && (this.mParent == null))
        {
            mDictionary[this.uid] = this;
            mList.Add(this);
            this.mIsRegistered = true;
        }
    }

    public void Remove(byte rfcID)
    {
        RemoveSavedRFC(this.uid, rfcID, null);
    }

    public void Remove(string rfcName)
    {
        RemoveSavedRFC(this.uid, 0, rfcName);
    }

    private static void RemoveSavedRFC(uint objID, byte rfcID, string funcName)
    {
        if (TNManager.isInChannel)
        {
            BinaryWriter writer = TNManager.BeginSend(Packet.RequestRemoveRFC);
            writer.Write(GetUID(objID, rfcID));
            if (rfcID == 0)
            {
                writer.Write(funcName);
            }
            TNManager.EndSend();
        }
    }

    public void Send(byte rfcID, string targetName, params object[] objs)
    {
        this.SendRFC(rfcID, null, targetName, true, objs);
    }

    public void Send(byte rfcID, Player target, params object[] objs)
    {
        if (target != null)
        {
            this.SendRFC(rfcID, null, target, true, objs);
        }
        else
        {
            this.SendRFC(rfcID, null, Target.All, true, objs);
        }
    }

    public void Send(byte rfcID, Target target, params object[] objs)
    {
        this.SendRFC(rfcID, null, target, true, objs);
    }

    public void Send(string rfcName, string targetName, params object[] objs)
    {
        this.SendRFC(0, rfcName, targetName, true, objs);
    }

    public void Send(string rfcName, Player target, params object[] objs)
    {
        if (target != null)
        {
            this.SendRFC(0, rfcName, target, true, objs);
        }
        else
        {
            this.SendRFC(0, rfcName, Target.All, true, objs);
        }
    }

    public void Send(string rfcName, Target target, params object[] objs)
    {
        this.SendRFC(0, rfcName, target, true, objs);
    }

    public void SendQuickly(byte rfcID, Player target, params object[] objs)
    {
        if (target != null)
        {
            this.SendRFC(rfcID, null, target, false, objs);
        }
        else
        {
            this.SendRFC(rfcID, null, Target.All, false, objs);
        }
    }

    public void SendQuickly(byte rfcID, Target target, params object[] objs)
    {
        this.SendRFC(rfcID, null, target, false, objs);
    }

    public void SendQuickly(string rfcName, Player target, params object[] objs)
    {
        this.SendRFC(0, rfcName, target, false, objs);
    }

    public void SendQuickly(string rfcName, Target target, params object[] objs)
    {
        this.SendRFC(0, rfcName, target, false, objs);
    }

    private void SendRFC(byte rfcID, string rfcName, string targetName, bool reliable, params object[] objs)
    {
        if (!string.IsNullOrEmpty(targetName))
        {
            if (targetName == TNManager.playerName)
            {
                if (rfcID != 0)
                {
                    this.Execute(rfcID, objs);
                }
                else
                {
                    this.Execute(rfcName, objs);
                }
            }
            else
            {
                BinaryWriter bw = TNManager.BeginSend(Packet.ForwardByName);
                bw.Write(targetName);
                bw.Write(GetUID(this.uid, rfcID));
                if (rfcID == 0)
                {
                    bw.Write(rfcName);
                }
                bw.WriteArray(objs);
                TNManager.EndSend(reliable);
            }
        }
    }

    private void SendRFC(byte rfcID, string rfcName, Player target, bool reliable, params object[] objs)
    {
        if (TNManager.isConnected)
        {
            BinaryWriter bw = TNManager.BeginSend(Packet.ForwardToPlayer);
            bw.Write(target.id);
            bw.Write(GetUID(this.uid, rfcID));
            if (rfcID == 0)
            {
                bw.Write(rfcName);
            }
            bw.WriteArray(objs);
            TNManager.EndSend(reliable);
        }
        else if (target == TNManager.player)
        {
            if (rfcID != 0)
            {
                this.Execute(rfcID, objs);
            }
            else
            {
                this.Execute(rfcName, objs);
            }
        }
    }

    private void SendRFC(byte rfcID, string rfcName, Target target, bool reliable, params object[] objs)
    {
        bool flag = false;
        if (target == Target.Broadcast)
        {
            if (TNManager.isConnected)
            {
                BinaryWriter bw = TNManager.BeginSend(Packet.Broadcast);
                bw.Write(GetUID(this.uid, rfcID));
                if (rfcID == 0)
                {
                    bw.Write(rfcName);
                }
                bw.WriteArray(objs);
                TNManager.EndSend(reliable);
            }
            else
            {
                flag = true;
            }
        }
        else if ((target == Target.Host) && TNManager.isHosting)
        {
            flag = true;
        }
        else if (TNManager.isInChannel)
        {
            if (!reliable)
            {
                if (target == Target.All)
                {
                    target = Target.Others;
                    flag = true;
                }
                else if (target == Target.AllSaved)
                {
                    target = Target.OthersSaved;
                    flag = true;
                }
            }
            byte packetID = (byte) (0x26 + target);
            BinaryWriter writer2 = TNManager.BeginSend(packetID);
            writer2.Write(GetUID(this.uid, rfcID));
            if (rfcID == 0)
            {
                writer2.Write(rfcName);
            }
            writer2.WriteArray(objs);
            TNManager.EndSend(reliable);
        }
        else if (!TNManager.isConnected && ((target == Target.All) || (target == Target.AllSaved)))
        {
            flag = true;
        }
        if (flag)
        {
            if (rfcID != 0)
            {
                this.Execute(rfcID, objs);
            }
            else
            {
                this.Execute(rfcName, objs);
            }
        }
    }

    private void Start()
    {
        if (this.id == 0)
        {
            this.mParent = FindParent(base.transform.parent);
            if (TNManager.isConnected && ((this.mParent == null) && Application.isPlaying))
            {
                UnityEngine.Debug.LogError("Objects that are not instantiated via TNManager.Create must have a non-zero ID.", this);
            }
        }
        else
        {
            this.Register();
            int index = 0;
            while (index < mDelayed.size)
            {
                DelayedCall call = mDelayed[index];
                if (call.objID == this.uid)
                {
                    if (!string.IsNullOrEmpty(call.funcName))
                    {
                        this.Execute(call.funcName, call.parameters);
                    }
                    else
                    {
                        this.Execute(call.funcID, call.parameters);
                    }
                    mDelayed.RemoveAt(index);
                }
                else
                {
                    index++;
                }
            }
        }
    }

    private void Unregister()
    {
        if (this.mIsRegistered)
        {
            if (mDictionary != null)
            {
                mDictionary.Remove(this.uid);
            }
            if (mList != null)
            {
                mList.Remove(this);
            }
            this.mIsRegistered = false;
        }
    }

    public bool isMine
    {
        get
        {
            return ((this.mOwner != -1) ? (this.mOwner == TNManager.playerID) : TNManager.isThisMyObject);
        }
    }

    public int ownerID
    {
        get
        {
            return ((this.mParent == null) ? this.mOwner : this.mParent.ownerID);
        }
    }

    public TNObject parent
    {
        get
        {
            return this.mParent;
        }
    }

    public uint uid
    {
        get
        {
            return ((this.mParent == null) ? ((uint) this.id) : this.mParent.uid);
        }
        set
        {
            if (this.mParent != null)
            {
                this.mParent.uid = value;
            }
            else
            {
                this.id = ((int) value) & 0xffffff;
            }
        }
    }

    [CompilerGenerated]
    private sealed class <EnsureDestroy>c__Iterator68 : IEnumerator, IDisposable, IEnumerator<object>
    {
        internal object $current;
        internal int $PC;
        internal TNObject <>f__this;

        [DebuggerHidden]
        public void Dispose()
        {
            this.$PC = -1;
        }

        public bool MoveNext()
        {
            uint num = (uint) this.$PC;
            this.$PC = -1;
            switch (num)
            {
                case 0:
                    this.$current = new WaitForSeconds(5f);
                    this.$PC = 1;
                    return true;

                case 1:
                    UnityEngine.Object.Destroy(this.<>f__this.gameObject);
                    this.$PC = -1;
                    break;
            }
            return false;
        }

        [DebuggerHidden]
        public void Reset()
        {
            throw new NotSupportedException();
        }

        object IEnumerator<object>.Current
        {
            [DebuggerHidden]
            get
            {
                return this.$current;
            }
        }

        object IEnumerator.Current
        {
            [DebuggerHidden]
            get
            {
                return this.$current;
            }
        }
    }

    private class DelayedCall
    {
        public byte funcID;
        public string funcName;
        public uint objID;
        public object[] parameters;
    }
}

